/**
    ******************************************************************************
    * @file    main.c 
    * @author  18408107475@163.com
    * @version v1.0
    * @date    2025/04/02
    * @brief   STM32 PWM呼吸灯控制程序
    * @details 
    * 功能特性：
    * - 使用TIM3定时器产生PWM信号
    * - 实现LED呼吸灯效果(渐亮渐暗)
    * - 提供精确延时函数(微秒/毫秒/秒级)
    * - 支持LED和蜂鸣器控制
    * 
    * 硬件连接：
    * - PB5连接LED1(通过PWM控制)
    * - PE5连接LED2(普通GPIO控制)
    * - PB8连接蜂鸣器
    * 
    * 注意事项：
    * - 系统时钟配置为72MHz
    * - 使用TIM3通道2部分重映射到PB5
    * - 呼吸灯效果通过改变PWM占空比实现
    ******************************************************************************
**/

/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f10x.h"  // STM32标准外设库，包含所有外设寄存器定义

/* 函数声明 ------------------------------------------------------------------*/
void Delay_ms(uint32_t xms);  // 毫秒延时函数声明
void Delay_us(uint32_t xus);  // 微秒延时函数声明
void Delay_s(uint32_t xs);    // 秒延时函数声明
void LED_STD_Config(void);    // LED初始化函数声明
void BEEP_STD_Config(void);   // 蜂鸣器初始化函数声明
void TIM3_PWM_Init(void);     // TIM3 PWM初始化函数声明

/* 全局变量 ------------------------------------------------------------------*/
// 此处可定义全局变量，当前程序未使用

/**
    ******************************************************************************
    * @brief  LED GPIO初始化
    * @param  无
    * @retval 无
    * @detail 配置PB5和PE5为推挽输出模式
    * 硬件连接：
    * - PB5连接LED1，低电平点亮(后续用于PWM控制)
    * - PE5连接LED2，低电平点亮(普通GPIO控制)
    * 初始状态：
    * - LED1熄灭（PB5高电平）
    * - LED2点亮（PE5低电平）
    ******************************************************************************
**/
void LED_STD_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    /* 开启GPIOB和GPIOE时钟（APB2总线） */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE);

    /* 配置PB5引脚 - 初始化为普通GPIO输出，后续会被PWM配置覆盖 */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 高速模式可改善边沿特性
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;  // 推挽输出直接驱动LED
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* 配置PE5引脚（复用同一结构体） */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    /* 设置初始状态 */
    GPIO_SetBits(GPIOB, GPIO_Pin_5);   // LED1初始熄灭
    GPIO_ResetBits(GPIOE, GPIO_Pin_5); // LED2初始点亮
}

/**
    ******************************************************************************
    * @brief  蜂鸣器初始化
    * @param  无
    * @retval 无
    * @detail 配置PB8为推挽输出，控制有源蜂鸣器
    * 注意：
    * - 适用于3.3V有源蜂鸣器，高电平触发
    * - 驱动无源蜂鸣器需改用PWM输出模式
    * - 初始化时会发出短暂测试音
    ******************************************************************************
**/
void BEEP_STD_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;     // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    // 高速模式确保快速响应
    GPIO_Init(GPIOB,&GPIO_InitStructure);

    GPIO_SetBits(GPIOB, GPIO_Pin_8);    // 初始高电平
    Delay_ms(100);                      // 延时100ms
    GPIO_ResetBits(GPIOB, GPIO_Pin_8);  // 关闭蜂鸣器(低电平)
}

/**
	******************************************************************************
	* @brief  微秒级延时
	* @param  xus 延时时长，范围：0 ~ 1864134 us
	* @note   使用SysTick定时器，基于9MHz时钟
	* @retval 无
	* @detail 工作原理：
	*   - SysTick为24位递减计数器
	*   - 计算公式：LOAD = (时钟频率/1000000) * xus - 1
	*   - 9MHz时钟时，1us需要9个时钟周期
	* 注意事项：
	*   - 最大延时受24位计数器限制
	*   - 不适用于在中断中调用
	*   - 使用时确保SysTick未被其他功能占用
	******************************************************************************
*/
void Delay_us(uint32_t xus)
{
    SysTick->CTRL = 0;                      // 关闭SysTick
    SysTick->LOAD = (9 * xus) - 1;          // 设置重装载值(9MHz时钟下)
    SysTick->VAL = 0x00;                    // 清除当前值
    SysTick->CTRL = 0x00000001;             // 启用SysTick（使用STCLK）
    while(!(SysTick->CTRL & 0x00010000));   // 等待COUNTFLAG置位
    SysTick->CTRL = 0;                      // 关闭SysTick
}

/**
	******************************************************************************
	* @brief  毫秒级延时
	* @param  xms 延时时长，范围：0~1864 ms
	* @note   基于Delay_us实现，1000us=1ms
	* @retval 无
	* 注意事项：
	*   - 实际最大延时受Delay_us限制
	*   - 存在函数调用开销，精度略低于直接使用SysTick
	*   - 适用于一般延时需求
	******************************************************************************
*/
void Delay_ms(uint32_t xms)
{
    while(xms--)
    {
        Delay_us(1000);  // 累计延时
    }
}

/**
	******************************************************************************
	* @brief  秒级延时
	* @param  xs 延时时长，范围：0~1 s
	* @note   基于Delay_ms实现，1000ms=1s
	* @retval 无
	* 注意事项：
	*   - 适用于非精确长延时
	*   - 长时间阻塞式延时影响系统实时性
	*   - 需要更精确的长延时建议使用定时器中断
	******************************************************************************
*/
void Delay_s(uint32_t xs)
{
    while(xs--)
    {
        Delay_ms(1000);  // 累计延时
    }
} 

/**
    ******************************************************************************
    * @brief  TIM3 PWM初始化
    * @param  无
    * @retval 无
    * @detail 配置TIM3通道2产生PWM信号控制PB5 LED
    * 参数配置：
    * - 时钟预分频: 7200 (72MHz/7200 = 10kHz)
    * - 自动重装载值: 100 (PWM频率 = 10kHz/100 = 100Hz)
    * - PWM模式: PWM模式2(CNT<CCR时输出有效)
    * - 输出极性: 高电平有效
    * 硬件连接：
    * - PB5通过TIM3_CH2控制LED
    * 工作原理：
    * - 通过改变CCR2值调整占空比
    * - 占空比 = CCR2 / (ARR+1)
    ******************************************************************************
**/
void TIM3_PWM_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    /* 1. 使能 TIM3 时钟（APB1 总线） */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    /* 2. 使能 GPIOB 和 AFIO 时钟（APB2 总线） */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

    /* 3. 配置 TIM3 通道2 部分重映射到 PB5 */
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);

    /* 4. 配置 PB5 为复用推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* 5. 配置 TIM3 时基单元 */
    TIM_TimeBaseStructure.TIM_Period = 100 - 1;      // ARR = 100（PWM 周期）
    TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1;  // PSC = 7200（72MHz / 7200 = 10kHz）
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;     // 无时钟分割
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    /* 6. 配置 TIM3 通道2 PWM 模式 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;       // PWM 模式2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;     // 高电平有效
    TIM_OCInitStructure.TIM_Pulse = 50;                     // 初始占空比 50%
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);

    /* 7. 使能 ARR 和 CCR2 预装载 */
    TIM_ARRPreloadConfig(TIM3, ENABLE);            // 使能自动重装载预装载
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能 CCR2 预装载

    /* 8. 启动 TIM3 */
    TIM_Cmd(TIM3, ENABLE);
}

/**
    ******************************************************************************
    * @brief  主函数
    * @param  无
    * @retval int 程序退出状态(实际不会退出)
    * @detail 程序主流程：
    * 1. 初始化PWM定时器
    * 2. 进入无限循环
    *   - 逐渐增加PWM占空比使LED渐亮
    *   - 逐渐减小PWM占空比使LED渐暗
    * 呼吸灯效果参数：
    * - PWM频率: 100Hz
    * - 亮度变化范围: 0-100%
    * - 变化速度: 每10ms变化1%
    ******************************************************************************
**/
int main(void)
{
    /* 初始化TIM3 PWM功能 */
    TIM3_PWM_Init();

    /* 主循环 */
    while(1)
    {
        /* 呼吸灯效果实现 */
        // 渐亮过程(占空比从0%增加到100%)
        for(int i=0; i<100; i++)
        {
            TIM_SetCompare2(TIM3, i);  // 设置占空比
            Delay_ms(10);              // 5ms延时
        }

        // 渐暗过程(占空比从100%减小到0%)
        for(int i=100; i>0; i--)
        {
            TIM_SetCompare2(TIM3, i);  // 设置占空比
            Delay_ms(10);              // 5ms延时
        }
    }
    
    // 程序理论上不会执行到这里
    return 0;
}

/*************************************END OF FILE**************************************/
